package com.nervenets.web.redis;

import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by Joe on 2017/12/16.
 */
@Component
public class RedisOrderedSetDao {
    @Resource
    private RedisTemplate redisTemplate;

    public <T extends RedisOrderedSetDomain> void saveOrUpdate(T domain) {
        redisTemplate.execute((RedisCallback) rc -> {
            rc.zAdd(domain.getRedisKey().getBytes(), domain.getOrderedValue(), domain.getField().getBytes());
            if (domain.getExpire() > 0) {
                rc.expire(domain.getRedisKey().getBytes(), domain.getExpire());
            }
            return true;
        });
    }

    public <T extends RedisOrderedSetDomain> void saveOrUpdate(T... domain) {
        for (T t : domain) {
            saveOrUpdate(t);
        }
    }

    public <T extends RedisOrderedSetDomain> Double getZScore(T domain) {
        return (Double) redisTemplate.execute((RedisCallback) rc -> rc.zScore(domain.getRedisKey().getBytes(), domain.getField().getBytes()));
    }

    public <T extends RedisOrderedSetDomain> Long getZCount(T domain, double min, double max) {
        return (Long) redisTemplate.execute((RedisCallback) rc -> rc.zCount(domain.getRedisKey().getBytes(), min, max));
    }

    public <T extends RedisOrderedSetDomain> Long getZCard(T domain) {
        return (Long) redisTemplate.execute((RedisCallback) rc -> rc.zCard(domain.getRedisKey().getBytes()));
    }

    public <T extends RedisOrderedSetDomain> Long getZRank(T domain) {
        return (Long) redisTemplate.execute((RedisCallback) rc -> rc.zRank(domain.getRedisKey().getBytes(), domain.getField().getBytes()));
    }

    public <T extends RedisOrderedSetDomain> Long getZRevRank(T domain) {
        return (Long) redisTemplate.execute((RedisCallback) rc -> rc.zRevRank(domain.getRedisKey().getBytes(), domain.getField().getBytes()));
    }

    public <T extends RedisOrderedSetDomain> Set<String> getZListAsc(T domain, long startPage, long pageSize) {
        return (Set<String>) redisTemplate.execute((RedisCallback) rc -> {
            Set<String> strings = new HashSet<>();
            final Set<byte[]> bytes = rc.zRange(domain.getRedisKey().getBytes(), (startPage - 1) * pageSize, (startPage - 1) * pageSize + (pageSize - 1));
            for (byte[] aByte : bytes) {
                strings.add(new String(aByte));
            }
            return strings;
        });
    }

    public <T extends RedisOrderedSetDomain> Set<String> getZListDesc(T domain, long startPage, long pageSize) {
        return (Set<String>) redisTemplate.execute((RedisCallback) rc -> {
            Set<String> strings = new HashSet<>();
            final Set<byte[]> bytes = rc.zRevRange(domain.getRedisKey().getBytes(), (startPage - 1) * pageSize, (startPage - 1) * pageSize + (pageSize - 1));
            for (byte[] aByte : bytes) {
                strings.add(new String(aByte));
            }
            return strings;
        });
    }

    public <T extends RedisOrderedSetDomain> Set<RedisZSetCommands.Tuple> getZListWithScoresDesc(T domain, long startPage, long pageSize) {
        return (Set<RedisZSetCommands.Tuple>) redisTemplate.execute((RedisCallback) rc -> rc.zRevRangeWithScores(domain.getRedisKey().getBytes(), (startPage - 1) * pageSize, (startPage - 1) * pageSize + (pageSize - 1)));
    }

    public <T extends RedisOrderedSetDomain> void delete(T domain) {
        redisTemplate.execute((RedisCallback) rc -> rc.zRem(domain.getRedisKey().getBytes(), domain.getField().getBytes()));
    }

    public Double incrBy(RedisOrderedSetDomain domain, long offset) {
        return (Double) redisTemplate.execute((RedisCallback) rc -> rc.zIncrBy(domain.getRedisKey().getBytes(), offset, domain.getField().getBytes()));
    }

    public void incr(RedisOrderedSetDomain domain) {
        redisTemplate.execute((RedisCallback) rc -> rc.zIncrBy(domain.getRedisKey().getBytes(), 1, domain.getField().getBytes()));
    }
}
